
package DB;

import Data.*;

import java.sql.PreparedStatement;
import java.rmi.RemoteException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Vector;

/**
 * Mapper Klasse, die Passanger Objekte auf eine relationale DB abbildet.
 * Diese Klasse erlaubt zahlreiche Operationen, um einen Passenger zu verwalten, z.B. eine LöschOperation oder eine EinfügeOperation
 * @author Stefan Fellner
 */
public class PassengerMapper {

    /*
     * Um sicher zu stellen, dass nur eine Instanz von der Klasse erzeugt wird, wird die Singleton Pattern angewendet.
     * Durch das Schlüsselwort static wird dies erreicht.
     * Quelle: http://www.theserverside.de/singleton-pattern-in-java/
     */
    private static PassengerMapper passengerMapper = null;

    private ConnectionManager manager;

    //mittels protected wird sichergestellt, dass mit new keine neuen Instanzen erstellt werden können. Sobals ein Mapper Objekt erstellt wird, wird eine connection aufgebaut
    protected PassengerMapper(){
        manager = ConnectionManager.getInstance();

    }

    /* Eine Instanz dieser Klasse sollte immer mittels der folgenden Methode erstellt werden.
     * Dadurch wird sicher gestellt, dass nur eine Instanz von dieser Klasse vorhanden ist.
     * @return DAS <code>PassengerMapper</code> Objekt
     * @see passengerMapper
     */
    public static PassengerMapper getInstance(){

        if(passengerMapper == null){
            passengerMapper = new PassengerMapper();
        }

        return passengerMapper;

    }


//Es folgen nun alle methoden, die die Tabelle passenger verändern können, bzw. deren inhalte abrufen können


    /* Ein Passenger in die Datenbank einfügen.
     * @param das Objekt welches hinzugefügt werden soll
     * @return das eingefügte Objekt.
     * Diese Methode wird den Primärschlüssel automatisch erzeugt. Der größte vorhandene Schlüssel wird um 1 erhöht.
     */
    public Passenger insert (Passenger p){
        //DB verbindung vom Connection Manager Objekt holen
        Connection con = this.manager.getConnection();

        try{
            // Leeres SQL-Statement (JDBC) anlegen
            Statement stmt = con.createStatement();

            /*
             * Zunächst schauen wir nach, welches der momentan größte
             * Primärschlüsselwert eines Passangers ist.
             */
            ResultSet maxId = stmt.executeQuery("SELECT MAX(passengerId) AS maxId " +"FROM passenger ");

            //die ergebnis Tabelle muss eine maxId zurückgeben.
            if(maxId.next()){
                try{
                
                 //Das einzufügende Objekt p erhält den neu errechneten Primärschlüssel
                 p.setPassengerId(maxId.getInt("maxId") + 1);

                    stmt = con.createStatement();

                     /**
                     * Jetzt erst erfolgt die tatsächliche Einfügeoperation.
                     * Die Attribute des Passengers, werden aus dem übergebenen Passenger mit den getter methoden geholt
                     */
                    stmt.executeUpdate("INSERT INTO passenger (passengerId,firstName,lastName,street,postCode,city)" +
                            "VALUES (" +
                            p.getPassengerId() + ", " + "'" +
                            p.getFirstName() + "'" + ", " + "'" +
                            p.getLastName() + "'" + ", " + "'" +
                            p.getStreet() + "'" + ", " + "'" +
                            p.getPostcode() + "'" + ", " + "'" +
                            p.getCity() + "'" + ")");
                }
                catch(RemoteException ex){
                    ex.printStackTrace();
                }
            }

        }
        //falls die sql abfrage fehlschlägt...
        catch (SQLException e2) {
            e2.printStackTrace();
        }
        //dieses return ist notwendig, da sich die ID des Passengers im Laufe der Methode verändert.
        return p;
    }







    /* Einen Passenger updaten, d.h. die Attribute des Passengers aktualisieren
     * @param das Passenger Objekt mit den neuen Attributen
     */
     public void update (Passenger p){

         //DB verbindung vom Connection Manager Objekt holen
        Connection con = this.manager.getConnection();

        try{
                //sql statement
            PreparedStatement stmt = con.prepareStatement("UPDATE passenger "+
                    "SET firstName="+"'" + p.getFirstName()+"'"+", "+
                    "lastName=" + "'" + p.getLastName()+"'"+", "+
                    "street=" + "'" + p.getStreet()+"'"+", "+
                    "postCode=" + "'" + p.getPostcode()+"'"+", "+
                    "city=" + "'" + p.getCity()+"'"+
                    " WHERE passengerId = " + "'" + p.getPassengerId() + "'");
            //dieser integer beinhaltet die anzahl der veränderten datensätze nach einer bestimmten UPDATE operation
            int anzahlVeraenderterDatensaetze = stmt.executeUpdate();
            }
            //falls der Zugriff auf die Klasse Passenger scheitert
            catch(RemoteException ex){
                ex.printStackTrace();
            }

        //falls die sql abfrage fehlschlägt...
        catch (SQLException e2) {
            e2.printStackTrace();
        }

     }


     /* Einen bestimmten Passenger in der Datenbank löschen. Die zugeordneten Bookings, werden über die Verwaltungsklasse gelöscht.
      * @param den zu löschenden Passenger
      */
     public void delete (Passenger p){

        //DB-Verbindung vom Connection Manager holen
        Connection con = this.manager.getConnection();

        try {
            // Leeres SQL-Statement anlegen
            Statement stmt = con.createStatement();

            stmt.executeUpdate("DELETE FROM passenger "+
                               "WHERE passengerId = "+"'"+p.getPassengerId()+"'");
        }
        //falls der Zugriff auf die Klasse Passenger fehlschlägt
        catch (RemoteException ex){
            ex.printStackTrace();
        }
        //falls der sql Befehl fehlschlägt
        catch (SQLException e2) {
            e2.printStackTrace();
        }

     }

     /*Alle Passengers, die in der Tabelle vorhanden sind werden zurückgegeben
      *
      *@return Vector mit allen Passengers
      */
     public Vector<Passenger> findAll(){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();
         //Vector, der alle Passenger Objekte festhält
         Vector<Passenger> allPassengers = new Vector<Passenger>();

         try{

             Statement stmt = con.createStatement();
             //das ergebnis der abfrage in ein ResultSet "Tabelle" abspeichern. D.h. jedes Tupel wird in eine Zeile des resultSets abgespeichert
             ResultSet allPassengersQueryResult = stmt.executeQuery("SELECT * FROM passenger "+"ORDER BY passengerId");

             //solange die ergebnisTabelle eine nachfolgende Zeile(Datensatz) hat, wird ein neues Passenger Objekt erstellt und zum ergebnisVector hinzugefügt.
             while(allPassengersQueryResult.next()){
                 //ein neues Passenger Objekt anlegen
                 Passenger p = new PassengerImpl(allPassengersQueryResult.getString("firstName"),
                                                allPassengersQueryResult.getString("lastName"),
                                                allPassengersQueryResult.getString("street"),
                                                allPassengersQueryResult.getInt("postCode"),
                                                allPassengersQueryResult.getString("city"));
                 //da der konstruktor das setzen der ID nicht zulässt, wird die ID über eine setter Methode gesetzt.
                 p.setPassengerId(allPassengersQueryResult.getInt("passengerId"));
                 //das gerade erstellte Objekt wird zum Ergebnisvektor hinzugefügt
                 allPassengers.addElement(p);
             }

         }
         catch(RemoteException ex){
             ex.printStackTrace();
         }
         catch(SQLException sqlE){
             sqlE.printStackTrace();
         }
         //der vector mit allen passengern aus der Tabelle wird zurückgegeben
         return allPassengers;
     }

     /*Alle Passengers, die in der Tabelle vorhanden sind und deren Nachname mit A-E beginnt, werden zurückgegeben
      *
      *@return Vector mit allen Passengers
      */
     public Vector<Passenger> findAllPassengersFromAtoE(){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();
         //Vector, der alle Passenger Objekte festhält
         Vector<Passenger> allPassengers = new Vector<Passenger>();

         try{

             Statement stmt = con.createStatement();
             //das ergebnis der abfrage in ein ResultSet "Tabelle" abspeichern. D.h. jedes Tupel wird in eine Zeile des resultSets abgespeichert
             ResultSet allPassengersQueryResult = stmt.executeQuery("SELECT * FROM passenger "+
                                                                    "WHERE lastName LIKE 'A%' "+
                                                                    "OR lastName LIKE 'B%' "+
                                                                    "OR lastName LIKE 'C%' "+
                                                                    "OR lastName LIKE 'D%' "+
                                                                    "OR lastName LIKE 'E%' "+
                                                                    "ORDER BY passengerId");

             //solange die ergebnisTabelle eine nachfolgende Zeile(Datensatz) hat, wird ein neues Passenger Objekt erstellt und zum ergebnisVector hinzugefügt.
             while(allPassengersQueryResult.next()){
                 //ein neues Passenger Objekt anlegen
                 Passenger p = new PassengerImpl(allPassengersQueryResult.getString("firstName"),
                                                allPassengersQueryResult.getString("lastName"),
                                                allPassengersQueryResult.getString("street"),
                                                allPassengersQueryResult.getInt("postCode"),
                                                allPassengersQueryResult.getString("city"));
                 //da der konstruktor das setzen der ID nicht zulässt, wird die ID über eine setter Methode gesetzt.
                 p.setPassengerId(allPassengersQueryResult.getInt("passengerId"));
                 //das gerade erstellte Objekt wird zum Ergebnisvektor hinzugefügt
                 allPassengers.addElement(p);
             }

         }
         catch(RemoteException ex){
             ex.printStackTrace();
         }
         catch(SQLException sqlE){
             sqlE.printStackTrace();
         }
         //der vector mit allen passengern aus der Tabelle wird zurückgegeben
         return allPassengers;
     }


     /*Alle Passengers, die in der Tabelle vorhanden sind und deren Nachname mit F-J beginnt, werden zurückgegeben
      *
      *@return Vector mit allen Passengers
      */
     public Vector<Passenger> findAllPassengersFromFtoJ(){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();
         //Vector, der alle Passenger Objekte festhält
         Vector<Passenger> allPassengers = new Vector<Passenger>();

         try{

             Statement stmt = con.createStatement();
             //das ergebnis der abfrage in ein ResultSet "Tabelle" abspeichern. D.h. jedes Tupel wird in eine Zeile des resultSets abgespeichert
             ResultSet allPassengersQueryResult = stmt.executeQuery("SELECT * FROM passenger "+
                                                                    "WHERE lastName LIKE 'F%' "+
                                                                    "OR lastName LIKE 'G%' "+
                                                                    "OR lastName LIKE 'H%' "+
                                                                    "OR lastName LIKE 'I%' "+
                                                                    "OR lastName LIKE 'J%' "+
                                                                    "ORDER BY passengerId");

             //solange die ergebnisTabelle eine nachfolgende Zeile(Datensatz) hat, wird ein neues Passenger Objekt erstellt und zum ergebnisVector hinzugefügt.
             while(allPassengersQueryResult.next()){
                 //ein neues Passenger Objekt anlegen
                 Passenger p = new PassengerImpl(allPassengersQueryResult.getString("firstName"),
                                                allPassengersQueryResult.getString("lastName"),
                                                allPassengersQueryResult.getString("street"),
                                                allPassengersQueryResult.getInt("postCode"),
                                                allPassengersQueryResult.getString("city"));
                 //da der konstruktor das setzen der ID nicht zulässt, wird die ID über eine setter Methode gesetzt.
                 p.setPassengerId(allPassengersQueryResult.getInt("passengerId"));
                 //das gerade erstellte Objekt wird zum Ergebnisvektor hinzugefügt
                 allPassengers.addElement(p);
             }

         }
         catch(RemoteException ex){
             ex.printStackTrace();
         }
         catch(SQLException sqlE){
             sqlE.printStackTrace();
         }
         //der vector mit allen passengern aus der Tabelle wird zurückgegeben
         return allPassengers;
     }


     /*Alle Passengers, die in der Tabelle vorhanden sind und deren Nachname mit K-O beginnt, werden zurückgegeben
      *
      *@return Vector mit allen Passengers
      */
     public Vector<Passenger> findAllPassengersFromKtoO(){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();
         //Vector, der alle Passenger Objekte festhält
         Vector<Passenger> allPassengers = new Vector<Passenger>();

         try{

             Statement stmt = con.createStatement();
             //das ergebnis der abfrage in ein ResultSet "Tabelle" abspeichern. D.h. jedes Tupel wird in eine Zeile des resultSets abgespeichert
             ResultSet allPassengersQueryResult = stmt.executeQuery("SELECT * FROM passenger "+
                                                                    "WHERE lastName LIKE 'K%' "+
                                                                    "OR lastName LIKE 'L%' "+
                                                                    "OR lastName LIKE 'M%' "+
                                                                    "OR lastName LIKE 'N%' "+
                                                                    "OR lastName LIKE 'O%' "+
                                                                    "ORDER BY passengerId");

             //solange die ergebnisTabelle eine nachfolgende Zeile(Datensatz) hat, wird ein neues Passenger Objekt erstellt und zum ergebnisVector hinzugefügt.
             while(allPassengersQueryResult.next()){
                 //ein neues Passenger Objekt anlegen
                 Passenger p = new PassengerImpl(allPassengersQueryResult.getString("firstName"),
                                                allPassengersQueryResult.getString("lastName"),
                                                allPassengersQueryResult.getString("street"),
                                                allPassengersQueryResult.getInt("postCode"),
                                                allPassengersQueryResult.getString("city"));
                 //da der konstruktor das setzen der ID nicht zulässt, wird die ID über eine setter Methode gesetzt.
                 p.setPassengerId(allPassengersQueryResult.getInt("passengerId"));
                 //das gerade erstellte Objekt wird zum Ergebnisvektor hinzugefügt
                 allPassengers.addElement(p);
             }

         }
         catch(RemoteException ex){
             ex.printStackTrace();
         }
         catch(SQLException sqlE){
             sqlE.printStackTrace();
         }
         //der vector mit allen passengern aus der Tabelle wird zurückgegeben
         return allPassengers;
     }

     /*Alle Passengers, die in der Tabelle vorhanden sind und deren Nachname mit P-T beginnt, werden zurückgegeben
      *
      *@return Vector mit allen Passengers
      */
     public Vector<Passenger> findAllPassengersFromPtoT(){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();
         //Vector, der alle Passenger Objekte festhält
         Vector<Passenger> allPassengers = new Vector<Passenger>();

         try{

             Statement stmt = con.createStatement();
             //das ergebnis der abfrage in ein ResultSet "Tabelle" abspeichern. D.h. jedes Tupel wird in eine Zeile des resultSets abgespeichert
             ResultSet allPassengersQueryResult = stmt.executeQuery("SELECT * FROM passenger "+
                                                                    "WHERE lastName LIKE 'P%' "+
                                                                    "OR lastName LIKE 'Q%' "+
                                                                    "OR lastName LIKE 'R%' "+
                                                                    "OR lastName LIKE 'S%' "+
                                                                    "OR lastName LIKE 'T%' "+
                                                                    "ORDER BY passengerId");

             //solange die ergebnisTabelle eine nachfolgende Zeile(Datensatz) hat, wird ein neues Passenger Objekt erstellt und zum ergebnisVector hinzugefügt.
             while(allPassengersQueryResult.next()){
                 //ein neues Passenger Objekt anlegen
                 Passenger p = new PassengerImpl(allPassengersQueryResult.getString("firstName"),
                                                allPassengersQueryResult.getString("lastName"),
                                                allPassengersQueryResult.getString("street"),
                                                allPassengersQueryResult.getInt("postCode"),
                                                allPassengersQueryResult.getString("city"));
                 //da der konstruktor das setzen der ID nicht zulässt, wird die ID über eine setter Methode gesetzt.
                 p.setPassengerId(allPassengersQueryResult.getInt("passengerId"));
                 //das gerade erstellte Objekt wird zum Ergebnisvektor hinzugefügt
                 allPassengers.addElement(p);
             }

         }
         catch(RemoteException ex){
             ex.printStackTrace();
         }
         catch(SQLException sqlE){
             sqlE.printStackTrace();
         }
         //der vector mit allen passengern aus der Tabelle wird zurückgegeben
         return allPassengers;
     }


     /*Alle Passengers, die in der Tabelle vorhanden sind und deren Nachname mit U-Z beginnt, werden zurückgegeben
      *
      *@return Vector mit allen Passengers
      */
     public Vector<Passenger> findAllPassengersFromUtoZ(){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();
         //Vector, der alle Passenger Objekte festhält
         Vector<Passenger> allPassengers = new Vector<Passenger>();

         try{

             Statement stmt = con.createStatement();
             //das ergebnis der abfrage in ein ResultSet "Tabelle" abspeichern. D.h. jedes Tupel wird in eine Zeile des resultSets abgespeichert
             ResultSet allPassengersQueryResult = stmt.executeQuery("SELECT * FROM passenger "+
                                                                    "WHERE lastName LIKE 'U%' "+
                                                                    "OR lastName LIKE 'V%' "+
                                                                    "OR lastName LIKE 'X%' "+
                                                                    "OR lastName LIKE 'Y%' "+
                                                                    "OR lastName LIKE 'Z%' "+
                                                                    "ORDER BY passengerId");

             //solange die ergebnisTabelle eine nachfolgende Zeile(Datensatz) hat, wird ein neues Passenger Objekt erstellt und zum ergebnisVector hinzugefügt.
             while(allPassengersQueryResult.next()){
                 //ein neues Passenger Objekt anlegen
                 Passenger p = new PassengerImpl(allPassengersQueryResult.getString("firstName"),
                                                allPassengersQueryResult.getString("lastName"),
                                                allPassengersQueryResult.getString("street"),
                                                allPassengersQueryResult.getInt("postCode"),
                                                allPassengersQueryResult.getString("city"));
                 //da der konstruktor das setzen der ID nicht zulässt, wird die ID über eine setter Methode gesetzt.
                 p.setPassengerId(allPassengersQueryResult.getInt("passengerId"));
                 //das gerade erstellte Objekt wird zum Ergebnisvektor hinzugefügt
                 allPassengers.addElement(p);
             }

         }
         catch(RemoteException ex){
             ex.printStackTrace();
         }
         catch(SQLException sqlE){
             sqlE.printStackTrace();
         }
         //der vector mit allen passengern aus der Tabelle wird zurückgegeben
         return allPassengers;
     }

     /* Passagiere mit einem bestimmten Namen zurück geben. Es können meherer Passagiere den gleichen Namen haben!
      * Der ErgebnisVector kann 0, 1 und n passagiere enthalten
      * @param den vollständigen Namen, dh. Vor- und Nachname
      * @return Vector, der alle Passagiere mit dem angegebenen Namen beinhaltet.
      */

     public Vector<Passenger> findByName(String firstName, String lastName){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();
         //Vector, der alle zurückgegebenen Passenger Objekte festhält
         Vector<Passenger> allPassengers = new Vector<Passenger>();

         try{

             Statement stmt = con.createStatement();

             ResultSet passengerList = stmt.executeQuery("SELECT * FROM passenger "+
                                                         "WHERE firstName = "+"'"+firstName+"' "+
                                                         "AND lastName = "+"'"+lastName+"'");

             //solange die ergebnisTabelle eine nachfolgende Zeile(Datensatz) hat, wird ein neues Passenger Objekt erstellt und zum ergebnisVector hinzugefügt.
             while(passengerList.next()){
                 //ein neues passenger Objekt anlegen
                 Passenger p = new PassengerImpl(passengerList.getString("firstName"),
                                                passengerList.getString("lastName"),
                                                passengerList.getString("street"),
                                                passengerList.getInt("postCode"),
                                                passengerList.getString("city"));
                 //da der konstruktor das setzen der ID nicht zulässt, wird die ID über eine setter Methode gesetzt.
                 p.setPassengerId(passengerList.getInt("passengerId"));
                 //das gerade erstellte Objekt wird zum Ergebnisvektor hinzugefügt
                 allPassengers.addElement(p);

            }
             
         }
         //falls der Zugriff auf die Klasse Passenger fehlschlägt
        catch (RemoteException ex){
            ex.printStackTrace();
            return null;
        }
        //falls der sql Befehl fehlschlägt
        catch (SQLException e2) {
            e2.printStackTrace();
            return null;
        }
        //den passenger vector mit allen zutreffenden passengern zurückgeben
         return allPassengers;
     }



     /* Einen bestimmten Passagier abhängig von der ID finden
      *
      * @param die PassengerID als integer
      * @return das gesuchte Passenger Objekt
      */

     public Passenger findById(int passengerId){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();

         try{

             Statement stmt = con.createStatement();

             ResultSet desiredPassenger = stmt.executeQuery("SELECT * FROM passenger "+
                                                           "WHERE passengerId = "+"'"+passengerId+"'");

             //es gibt nur einen Passenger mit gleicher ID. Es wird zunächst geschaut ob, ein Passenger in der ResultSet "Tabelle" ist
             if(desiredPassenger.next()){
                 //den ergebnisDatensatz in ein Objekt umwandeln
                 Passenger p = new PassengerImpl(desiredPassenger.getString("firstName"),desiredPassenger.getString("lastName"),desiredPassenger.getString("street"),desiredPassenger.getInt("postCode"),desiredPassenger.getString("city"));
                 p.setPassengerId(desiredPassenger.getInt("passengerId"));
                 //den gefundenen passagier zurückgeben
                 return p;
             }

         }
         //falls der Zugriff auf die Klasse Passenger fehlschlägt
        catch (RemoteException ex){
            ex.printStackTrace();
            return null;
        }
        //falls der sql Befehl fehlschlägt
        catch (SQLException e2) {
            e2.printStackTrace();
            return null;
        }
        //wenn keine Passenger gefunden wurde und keine Fehlermeldung ausgegeben wurde, gibt die Methode null zurück.
         return null;
     }





}